﻿package racingGame.models 
{
	import away3d.core.base.Mesh;
	import away3d.core.base.Vertex;
	import away3d.core.geom.Line2D;
	import away3d.core.math.Number3D;
	import away3d.loaders.data.GeometryData;
	import away3d.materials.BitmapMaterial;
	import away3d.materials.ITriangleMaterial;
	import away3d.materials.utils.data.Ray;
	import away3d.materials.WireColorMaterial;
	import collada.DAE;
	import racingGame.assets.Textures;
	import racingGame.utils.MathUtils;
	
	/**
	 * ...
	 * @author Jorge Miranda
	 */
	public class Track 
	{		
		/**** Private Static Variables ****/
		
		private static var  TRACK_WIDTH	:Number = 12;
		private static var  DEFAULT_SEGMENT_LENGTH	:Number = 12;
		
		/**** Private Variables ****/
		
		private var _mesh				:Mesh;
		
		private var _trackPoints 		:Vector.<Number3D>;
		private var _trackLenght 		:uint;
		private var _road_material		:ITriangleMaterial;
		private var _guardrail_material	:ITriangleMaterial;
		private var _segments			:Vector.<TrackSegment>;
		private var _track_length		:Number;
		
		
		/**** Public Properties ****/		
		
		public function get mesh():Mesh { return _mesh; }
		
		public function get segments():Vector.<TrackSegment> { return _segments; }
		
		
		/**** Public Static Methods ****/
		
		static public function createFromDAE(dae:DAE) :Track
		{
			
			var trackPoints:Vector.<Number3D> = parseDAE(dae);
			
			return new Track(trackPoints);		
		}
		
		static private function parseDAE(dae:DAE):Vector.<Number3D>
		{
			
			var trackPoints:Vector.<Number3D> = new Vector.<Number3D>();
			
			var geometry:GeometryData = dae.mesh.geometryLibrary.getGeometry("track-mesh");
			
			for (var i:int = 0; i < geometry.vertices.length; i++) 
			{
				trackPoints.push(Vertex(geometry.vertices[i]).position);
			}
			
			return trackPoints;
		}
		
		
		/**** Public Methods ****/
		
		/**
		 * Constructor.
		 */
		public function Track(points:Vector.<Number3D>) 
		{
			
			_trackPoints = points;
			
			initializeTrackPoints();
			
			initializeMaterials();
			
			initializeMesh();	
			
		}
		
		private function initializeMaterials():void
		{
			_guardrail_material = new BitmapMaterial(Textures.GUARDRAIL);
			_road_material = new BitmapMaterial(Textures.ROAD);
			(_road_material as BitmapMaterial).repeat = true;
			(_guardrail_material as BitmapMaterial).repeat = true;
		}
		
		
		/* 
		 * Initialize Track Points
		 * 
		 * TODO: I think that in a future this method will do more things... If it doesn't happen, just remove this method.
		 */
		private function initializeTrackPoints():void
		{
			
			_trackLenght = _trackPoints.length;
		}
		
		
		/*
		 * Creates the track mesh, including its UVW map.
		 * 
		 * TODO: Also, generate the guard rails meshes.
		 */		
		private function initializeMesh():void
		{
			
			_segments = new Vector.<TrackSegment>();
			
			_mesh = new Mesh();
			_mesh.bothsides = true;
			
			_track_length = 0;
			var textureTileStart :Number = 0;
			
			for (var i:int = 0; i < _trackLenght; i++) 
			{
				
				var trackSegment :TrackSegment = new TrackSegment(_trackPoints[i], _trackPoints[(i != _trackLenght -1)? i + 1 : 0], TRACK_WIDTH,_road_material, _guardrail_material, textureTileStart, DEFAULT_SEGMENT_LENGTH);
				
				_segments.push(trackSegment);
				
				for (var n:int = 0; n < trackSegment.faces.length; n++) 
				{
					_mesh.addFace(trackSegment.faces[n]);
				}
				
				// First (incorrect) approach for the track's smooth.
				
				/*	
				 *   v1	----B---- v3
				 *   	|\		|
				 *		| \		|
				 *		|  \	|
				 *		|   \	|
				 *		|    \	|	 
				 *		|     \	|
				 *		|      \|
				 *	 v0	----A---C v2
				 *		 r->
				 */
				
				if (i > 0)
				{
					unifyTrackSegments(_segments[i], _segments[i - 1])
				}	
				
				_track_length += trackSegment.length;			
				
				textureTileStart = (_track_length / DEFAULT_SEGMENT_LENGTH) - (Math.floor(_track_length / DEFAULT_SEGMENT_LENGTH))
			}
			
			unifyTrackSegments(_segments[0], _segments[_segments.length - 1]); // Unify the first segment with the last one.
			
			trace("TRACK LENGTH : " + _track_length);
		}
		
		private function unifyTrackSegments(segmentA :TrackSegment, segmentB :TrackSegment):void
		{			
			var leftSideIntersection:Number3D = MathUtils.getLineLineIntersection(segmentA.v0, segmentA.v1, segmentB.v0, segmentB.v1);
			var rightSideIntersection:Number3D = MathUtils.getLineLineIntersection(segmentA.v2, segmentA.v3, segmentB.v2, segmentB.v3);
			
			// TODO: Evaluate which is the best approach to select the Y coord
			
			//leftSideIntersection.y = segmentA.v0.y + ( segmentB.v1.y -segmentA.v0.y) / 2;
			//rightSideIntersection.y = segmentA.v2.y + ( segmentA.v3.y -segmentA.v2.y) / 2;
			
			leftSideIntersection.y = segmentA.v0.y ;
			rightSideIntersection.y = segmentA.v2.y;
			
			segmentA.v0 = leftSideIntersection;
			segmentB.v1 = leftSideIntersection;
			
			segmentA.v2 = rightSideIntersection;
			segmentB.v3 = rightSideIntersection;
		}
	}
}